/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkHttpMessage.h"
#include "MkUtil/MkUtil.h"
#include "MkUtil/MkLog.h"

CMkHttpMessage::CMkHttpMessage()
    : m_Version("1.1")
    , m_ParseHeaderEnd(FALSE)
    , m_BodySize(0)
    , m_bChunkedBody(FALSE)
{

}

//only parse header
Uint32 CMkHttpMessage::Parse(const Uint8* pBuf, Uint32 nLen, Uint32& nUsed)
{
    Uint32 ErrorCode = NoneError;
    Uint32 nPos = 0;
    if (!m_ParseHeaderEnd) {
        MkString strLine;
        ErrorCode = CMkUtil::ReadLine((const Int8*)pBuf, nLen, nPos, strLine);
        MkCheckErrorLog(ErrorCode, "read start line failed buf:%s\n", pBuf);

        ErrorCode = OnParseHeaderLine(strLine);
        MkCheckErrorLog(ErrorCode, "parse start line:%s failed\n", strLine.c_str());

        MkString strKey;
        MkString strValue;
        while (1) {
            ErrorCode = CMkUtil::ReadLine((const Int8*)pBuf, nLen, nPos, strLine);
            if (NoneError != ErrorCode) {
                MkCheckErrorLog(ErrorCode, "read line failed  content:%s\n", pBuf);
                ErrorCode = NotHasEnoughBuffer;
                break;
            }
            if (strLine.empty()) {
                break;
            }
            ErrorCode = ParseHeader(strLine, strKey, strValue);
            if (NoneError != ErrorCode) {
                MkWarningLog("parse head:%s failed\n", strLine.c_str());
                ErrorCode = InvalidParams;
                break;
            } else {
                m_Headers.insert(std::pair<MkString, MkString>(strKey, strValue));
            }
        }
        if (NoneError == ErrorCode) {
            nUsed = nPos;
            ErrorCode = GetBodySize();
            m_ParseHeaderEnd = TRUE;
        }
    }
    if (m_ParseHeaderEnd) {
        if (!m_bChunkedBody) {
            Uint32 nNeed = m_BodySize - m_Body.size();
            if (nLen - nUsed < nNeed) {
                m_Body += MkString((Int8*)pBuf + nUsed, nLen - nUsed);
                nUsed = nLen;
                ErrorCode = NotHasEnoughBuffer;
            } else {
                m_Body += MkString((Int8*)pBuf + nUsed, nNeed);
                nUsed += nNeed;
                ErrorCode = NoneError;
            }
        } else {
            MkString Content = MkString((Int8*)pBuf + nUsed, nLen - nUsed);
            Uint32 nParsed = 0;
            MkString strLine;
            m_BodySize = 0;
            while (1) {
                ErrorCode = CMkUtil::BufferReadLine(Content, nParsed, strLine);
                if (NoneError != ErrorCode)
                {
                    //MkErrorLog("read body len failed\n");
                    m_BodySize = -1;
                    m_Body.clear();
                    return NotHasEnoughBuffer;
                }               
                Uint8 pTmp[Len16] = { 0 };
                Uint32 nTmpLen = Len16;
                Uint16 nTmpSize = 0;
                CMkUtil::StringToByte(strLine, pTmp, nTmpLen);
                for (Uint32 i = 0; i < nTmpLen; i++) {
                    nTmpSize |= pTmp[i] << ((nTmpLen - i - 1) * 8);
                }
                if (0 != nTmpSize) {
                    m_BodySize += nTmpSize;
                    m_Body += Content.substr(nParsed, nTmpSize);
                    nParsed += nTmpSize + 2;
                }
                if (strLine.empty()) {
                    break;
                }
            }
            nUsed += nParsed;
        }
    }
    return ErrorCode;
}

BOOL CMkHttpMessage::IsConnectClose() const
{
    MkString Value;
    Uint32 ErrorCode = GetHeadValue("Connection", Value);
    return NoneError == ErrorCode && "close" == Value;
}

Uint32 CMkHttpMessage::ToString(MkString& Content)
{
    Uint32 ErrorCode = GetHeaderLine(Content);
    MkCheckErrorLog(ErrorCode, "get header line failed\n");
    MkMultiMap<MkString, MkString>::iterator it = m_Headers.begin();
    for (; it != m_Headers.end(); it++) {
        Content += it->first + ": " + it->second + MkEndLine;
    }

    Content += MkEndLine;
    return NoneError;
}

Uint32 CMkHttpMessage::GetHeadValue(const MkString& Key, MkString& Value) const
{
    MkMultiMap<MkString, MkString>::const_iterator it = m_Headers.find(Key);
    if (it != m_Headers.end()) {
        Value = it->second;
        return NoneError;
    }
    return InvalidParams;
}

Uint32 CMkHttpMessage::AddHeader(const MkString& Key, const MkString& Value)
{
    m_Headers.insert(std::pair<MkString, MkString>(Key, Value));
    return NoneError;
}

Uint32 CMkHttpMessage::SetVersion(const MkString& Version)
{
    m_Version = Version;
    return NoneError;
}

void CMkHttpMessage::Release()
{
    m_Headers.clear();
    m_Version = "1.1";
    m_Body.clear();
    m_BodySize = 0;
    m_ParseHeaderEnd = FALSE;
    m_bChunkedBody = 0;
    m_HttpSessionId = 0;
}

Uint32 CMkHttpMessage::ParseHeader(const MkString& strHeader, MkString& Key, MkString& Value)
{
    MkVector<MkString> VecString;
    Uint32 ErrorCode = CMkUtil::StringSplit(strHeader, ": ", VecString);
    MkCheckErrorLog(ErrorCode, "split string:%s failed\n", strHeader.c_str());
    if (VecString.size() < 2) {
        return InvalidParams;
    }
    Key = VecString[0];
    Value = VecString[1];
    return NoneError;
}

Uint32 CMkHttpMessage::GetBodySize()
{
    MkString ContentLength;
    MkString Connection;
    MkString Encoding;
    GetHeadValue("Content-Length", ContentLength);
    GetHeadValue("Connection", Connection);
    GetHeadValue("Transfer-Encoding", Encoding);
    if (!ContentLength.empty()) {
        m_BodySize = std::stoi(ContentLength);
    } else if ("chunked" == Encoding) {
        m_bChunkedBody = TRUE;
        m_BodySize = InvalidSize;
    } else if ("Close" != Connection) {
        m_BodySize = 0;
    } else {
        return InvalidParams;
    }
    return NoneError;
}

CMkHttpMessage::~CMkHttpMessage()
{
    m_Headers.clear();
}